package orchestration

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/sirupsen/logrus"
	"math/rand"
	"openstack_api/client"
	my_common "openstack_api/common"
	"openstack_api/exceptions"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"time"
)

const screenshotPath = "/var/www/vncscreenshot/"
const screencapPath = "/var/www/vncscreencap/"

type Server struct {
	*servers.Server
	BlockDeviceMappingV2 []volumeattach.VolumeAttachment `json:"block_device_mapping_v2,omitempty"`
	ServerID             string                          `json:"server_id,omitempty"`
}

//type ServerID struct {
//	ServerId string `json:"server_id"`
//}

type ActionCallback struct {
	ServerId    string `json:"server_id,omitempty"`
	TaskState   string `json:"OS-EXT-STS:task_state,omitempty"`
	VmState     string `json:"OS-EXT-STS:vm_state,omitempty"`
	PowerState  int    `json:"OS-EXT-STS:power_state,omitempty"`
	Status      string `json:"status,omitempty"`
	CallbackUrl string `json:"callback_url"`
}

type ScreenCapParam struct {
	Seconds int `json:"seconds"`
}

type ServerWithExt struct {
	servers.Server
	extendedstatus.ServerExtendedStatusExt
}

func (s Server) CreateServer(c *gin.Context) {
	//没有，使用stack创建

}

func (s Server) GetConsole(c *gin.Context) {
	serverClient := client.GetServerClient()
	serverClient.Microversion = "2.6"

	createOpts := remoteconsoles.CreateOpts{
		Protocol: remoteconsoles.ConsoleProtocolVNC,
		Type:     remoteconsoles.ConsoleTypeNoVNC,
	}
	SID := c.Param("uuid")
	//sid := ActionCallback{}
	//err := c.ShouldBindJSON(&sid)
	//if err != nil {
	//	s.errRet(c, 500, err.Error())
	//	return
	//}
	//isValidate := exceptions.IsValidUUID(s.ServerID)
	_, err := servers.Get(serverClient, SID).Extract()
	if err != nil {
		s.errRet(c, 404, err.Error())
		return
	}
	if SID != "" {
		remoteConsole, err := remoteconsoles.Create(serverClient, SID, createOpts).Extract()
		if err != nil {
			s.errRet(c, 401, err.Error())
			return
		}
		fmt.Printf("Console URL: %s\n", remoteConsole.URL)
		c.JSON(200, remoteConsole)
	} else {
		s.errRet(c, 404, "Please input 'server_id', it is a uuid")
		return
	}

}

func (s Server) errRet(c *gin.Context, code int, message interface{}) {
	c.JSON(code, gin.H{
		"code":    code,
		"message": message,
	})
}

func (s Server) GetServer(c *gin.Context) {
	serverClient := client.GetServerClient()
	serverClient.Microversion = "2.88"
	SID := c.Param("uuid")
	//if exceptions.IsValidUUID(SID) {
	//	s.errRet(c, 404, "Please input 'server_id', it is a uuid")
	//	return
	//}
	server, err := servers.Get(serverClient, SID).Extract()

	if err != nil {
		var errorDetail exceptions.ServerErrorDetails
		extractErr, code := exceptions.ServerExtractErrorInto(err, &errorDetail)
		c.JSON(code, extractErr)
		return
	}
	s.Server = server
	allPagers, err := volumeattach.List(serverClient, SID).AllPages()
	if err == nil {
		s.BlockDeviceMappingV2, err = volumeattach.ExtractVolumeAttachments(allPagers)
	}
	//new_server.BlockDevice =
	c.JSON(200, s)
	return
}

func (s Server) GetBlockDeviceMapping() {}

func (s Server) ListServers(c *gin.Context) {
	allParam := c.Request.URL.Query()
	fmt.Println(allParam)
	var listOpts servers.ListOpts
	paramToMap := make(map[string]string)
	for key, value := range allParam {
		paramToMap[key] = value[0]
	}
	paramJson, err := json.Marshal(paramToMap)
	if err != nil {
		s.errRet(c, 500, err.Error())
	}
	err = json.Unmarshal(paramJson, &listOpts)
	if err != nil {
		s.errRet(c, 500, err.Error())
	}
	fmt.Println(listOpts)
	allServers, err := s.listServers(listOpts)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	c.JSON(200, allServers)
	return
}

func (s Server) listServers(opts servers.ListOpts) (allServers []servers.Server, err error) {
	fmt.Println(opts)
	serverCLient := client.GetServerClient()
	allPagers, err := servers.List(serverCLient, opts).AllPages()
	if err != nil {
		return nil, err
	}
	allImages, err := servers.ExtractServers(allPagers)
	return allImages, err
}

func (s Server) StopServer(c *gin.Context) {
	serverCLient := client.GetServerClient()
	SID := c.Param("uuid")
	//if exceptions.IsValidUUID(SID) {
	//	s.errRet(c, 404, "Please input 'server_id', it is a uuid")
	//	return
	//}
	ac := ActionCallback{}
	//_ = c.ShouldBindJSON(&ac)
	ac.ServerId = SID
	server, err := servers.Get(serverCLient, SID).Extract()
	if err != nil {
		var errorDetail exceptions.ServerErrorDetails
		extractErr, code := exceptions.ServerExtractErrorInto(err, &errorDetail)
		c.JSON(code, extractErr)
		return
	}
	err = startstop.Stop(serverCLient, SID).ExtractErr()
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	server, err = servers.Get(serverCLient, SID).Extract()
	go s.actionCallback(ac, "stop", "stopped")
	c.JSON(200, server)
	return
}

func (s Server) StartServer(c *gin.Context) {
	serverCLient := client.GetServerClient()
	SID := c.Param("uuid")
	//if exceptions.IsValidUUID(SID) {
	//	s.errRet(c, 404, "Please input 'server_id', it is a uuid")
	//	return
	//}
	ac := ActionCallback{}
	//_ = c.ShouldBindJSON(&ac)
	ac.ServerId = SID
	server, err := servers.Get(serverCLient, SID).Extract()
	if err != nil {
		var errorDetail exceptions.ServerErrorDetails
		extractErr, code := exceptions.ServerExtractErrorInto(err, &errorDetail)
		c.JSON(code, extractErr)
		return
	}
	err = startstop.Start(serverCLient, SID).ExtractErr()
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	server, err = servers.Get(serverCLient, SID).Extract()
	go s.actionCallback(ac, "start", "active")
	c.JSON(200, server)
	return
}

func (s Server) RebootServer(c *gin.Context) {
	serverCLient := client.GetServerClient()
	SID := c.Param("uuid")
	//if exceptions.IsValidUUID(SID) {
	//	s.errRet(c, 500, "Please input 'server_id', it is a uuid")
	//	return
	//}
	ac := ActionCallback{}
	//_ = c.ShouldBindJSON(&ac)
	ac.ServerId = SID
	server, err := servers.Get(serverCLient, SID).Extract()
	if err != nil {
		var errorDetail exceptions.ServerErrorDetails
		extractErr, code := exceptions.ServerExtractErrorInto(err, &errorDetail)
		c.JSON(code, extractErr)
		return
	}
	rebootOpts := servers.RebootOptsBuilder(servers.RebootOpts{
		Type: servers.HardReboot,
	})
	err = servers.Reboot(serverCLient, SID, rebootOpts).ExtractErr()
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	server, err = servers.Get(serverCLient, SID).Extract()
	go s.actionCallback(ac, "reboot", "active")
	c.JSON(200, server)
	return
}

func (s Server) actionCallback(ac ActionCallback, action string, expectStatus string) {
	times := 0
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	for {
		//time.Sleep(time.Minute / 3)
		times += 1
		server = ServerWithExt{}
		err := servers.Get(serverCLient, ac.ServerId).ExtractInto(&server)
		if err != nil {
			return
		}
		logrus.Info("Server action callback start: server ID=", ac.ServerId, ", current status=", server.ServerExtendedStatusExt.VmState, ", current task_state=", server.ServerExtendedStatusExt.TaskState)
		if server.ServerExtendedStatusExt.VmState != expectStatus {
			if (times >= 20) || strings.Contains(server.Status, "ERROR") || strings.Contains(server.Status, "FAILED") {
				break
			}
		} else {
			break
		}
		if server.ServerExtendedStatusExt.TaskState == "" {
			break
		}
		time.Sleep(time.Minute / 12)
	}
	logrus.Info("Server action callback loop end: server ID=", ac.ServerId, ", current status=", server.ServerExtendedStatusExt.VmState)
	//ac.Status = server.Status
	ac.VmState = server.ServerExtendedStatusExt.VmState
	ac.PowerState = int(server.ServerExtendedStatusExt.PowerState)
	//if ac.VmState == expectStatus {
	//	ac.Status = "UPDATE_COMPLETE"
	//} else {
	//	ac.Status = "UPDATE_FAILED"
	//}
	ac.Status = server.Status
	logrus.Info("Server action callback loop end: server ID=", ac.ServerId, ", callback data =", ac)
	data, _ := json.Marshal(ac)
	logrus.Info("Server callback start: callback_data=", string(data))
	body, statuscode, header, e := my_common.HttpPostJson(data, ac.CallbackUrl)
	if statuscode != 200 {
		logrus.Error("Server action callback error: server id=", ac.ServerId, ",status_code=", statuscode, ", response=", body, ",header=", header, ", error=", e)
	}
	logrus.Info("Server action callback success: server id=", ac.ServerId, ",status_code=", statuscode, ", response=", body, ",header=", header, ", error=", e)

}

// GetInstanceFile 获取目标文件的内容，该接口不对外开放，只用于剪切板功能，获取特定文件的内容。
func (s Server) GetInstanceFile(c *gin.Context) {
	serverCLient := client.GetServerClient()
	SID := c.Param("uuid")
	_, err := servers.Get(serverCLient, SID).Extract()
	logrus.Info("Get instance file start, server=", SID)
	if err != nil {
		var errorDetail exceptions.ServerErrorDetails
		extractErr, code := exceptions.ServerExtractErrorInto(err, &errorDetail)
		c.JSON(code, extractErr)
		return
	}

	//opts := client.InstanceFileOpts{}
	//err = c.ShouldBindJSON(&opts)
	if err != nil {
		logrus.Error("Get instance file error, server=", SID, ",error=", err)
		s.errRet(c, 500, err.Error())
		return
	}
	resp := client.GetInstanceFile(serverCLient, SID)
	if resp.Err != nil {
		logrus.Error("Get instance file error, server=", SID, ",error=", err)
		s.errRet(c, 500, resp.Err.Error())
		return
	}
	ret := resp.Body
	logrus.Info("Get instance file end, server=", SID, ",resp=", ret)
	c.JSON(200, ret)
}

// PostInstanceFile 传输文件到虚拟机中，需要nova中开发的了os-instance-file接口
func (s Server) PostInstanceFile(c *gin.Context) {
	serverCLient := client.GetServerClient()
	SID := c.Param("uuid")
	_, err := s.getServer(SID)
	logrus.Info("POST instance file start, server=", SID)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}

	opts := client.InstanceFileOpts{}
	postInstanceFileParams := struct {
		File        string `json:"file" required:"false"`
		Content     string `json:"content" required:"false"`
		Url         string `json:"url" required:"false"`
		CallbackUrl string `json:"callback_url" required:"false"`
	}{}
	err = c.ShouldBindJSON(&postInstanceFileParams)
	logrus.Info("POST instance file process, server=", SID, ",opts=", postInstanceFileParams)
	if err != nil {
		logrus.Error("POST instance file error, server=", SID, ",error=", err.Error())
		s.errRet(c, 500, err.Error())
		return
	}

	opts.File = postInstanceFileParams.File
	opts.Url = postInstanceFileParams.Url
	opts.Content = postInstanceFileParams.Content
	resp := client.PostInstanceFile(serverCLient, SID, opts)
	if resp.Err != nil {
		logrus.Error("POST instance file error, server=", SID, ",error=", resp.Err.Error())
		s.errRet(c, 500, resp.Err.Error())
		return
	}
	request_id := resp.Header.Get("x-openstack-request-id")
	c.JSON(200, map[string]string{"request_id": request_id})
	//c.JSON(202, "123")
	if postInstanceFileParams.CallbackUrl != "" {
		go s.InstanceFileCallback(SID, request_id, postInstanceFileParams.CallbackUrl)
	}
	logrus.Info("POST instance file end, server=", SID, ",ret=", map[string]string{"request_id": request_id})

	return
}

func (s Server) InstanceFileCallback(SID string, request_id string, callbackUrl string) {
	//serverCLient := client.GetServerClient()
	err := s.waitForServerTaskState(SID, "SEND_FILE", "", 1800)
	//var ret map[string]interface{}
	//ret = make(map[string]interface{})
	ret := struct {
		ServerID   string                   `json:"server_id"`
		RequestID  string                   `json:"request_id"`
		Error      interface{}              `json:"error"`
		Events     *[]instanceactions.Event `json:"events"`
		StartTime  time.Time                `json:"start_time"`
		FinishTime *time.Time               `json:"finish_time"`
	}{}
	ret.ServerID = SID
	ret.RequestID = request_id
	ret.Error = ""
	if err != nil {
		ret.Error = err
	}
	resp, err := s.getInstanceAction(SID, request_id)
	if err != nil {
		ret.Error = err
	}
	if resp.Message == "Error" {
		ret.Error = resp.Message
	}
	ret.StartTime = resp.StartTime
	ret.Events = resp.Events
	ret.FinishTime = resp.UpdatedAt
	data, _ := json.Marshal(ret)
	body, statuscode, header, e := my_common.HttpPostJson(data, callbackUrl)
	if statuscode != 200 {
		logrus.Error("Instance File  callback error: request_id ", request_id, ",status_code=", statuscode, ", response=", string(body), ",header=", header, ", error=", e)
	}
	logrus.Info("Instance File callback end: request_id ", request_id, ",status_code=", statuscode, ", response=", string(body), ",header=", header, ", error=", e)

}

//func(s Server) getInstanceAction{
//
//}

func (s Server) getInstanceAction(sid string, requestId string) (resp instanceactions.InstanceActionDetail, err error) {
	serverCLient := client.GetServerClient()
	serverCLient.Microversion = "2.87"
	resp, err = instanceactions.Get(serverCLient, sid, requestId).Extract()
	return
}

func (s Server) waitForServerTaskState(sid string, shouldBeStatus string, stopStatus string, secs int) error {
	return gophercloud.WaitFor(secs, func() (bool, error) {
		time.Sleep(5)
		server, err := s.getServer(sid)
		if err != nil {
			return false, err
		}

		if err != nil {
			return false, err
		}

		if server.TaskState == stopStatus {
			return true, nil
		} else if server.TaskState != shouldBeStatus {
			return false, errors.New(fmt.Sprintf("Expect task_state is wrong. current task_state is '%s', it should be '%s'. The process stop task_state should be '%s'。", server.TaskState, shouldBeStatus, stopStatus))
		}

		return false, nil
	})
}

// GetInstanceFileState 用于获取传输文件的状态，没有进度
func (s Server) GetInstanceFileState(c *gin.Context) {
	SID := c.Param("uuid")
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	//if server.TaskState == "SEND_FILE" {
	//	c.JSON(202, map[string]string{"status": "in-progress"})
	//} else {
	//	c.JSON(202, map[string]string{"status": "complete"})
	//}
}

func (s Server) getServer(serverID string) (server ServerWithExt, err error) {
	serverCLient := client.GetServerClient()
	server = ServerWithExt{}
	err = servers.Get(serverCLient, serverID).ExtractInto(&server)
	//if err != nil {
	//	var errorDetail exceptions.ServerErrorDetails
	//	extractErr, code := exceptions.ServerExtractErrorInto(err, &errorDetail)
	//}
	return
}

//func (s Server) Metrics(c *gin.Context) {
//	SID := c.Param("uuid")
//	server := s.getServer(SID, c)
//	if server == nil {
//		return
//	}
//	gnocchiClient := client.GetGnocchiClient()
//
//	fmt.Println(gnocchiClient)
//
//	metricID = "ff88b529-b4e3-4e64-8772-10546db4f0d8"
//	startTime := time.Date(2022, 7, 4, 10, 0, 0, 0, time.UTC)
//
//	listOpts := measures.ListOpts{
//		Resample:    "2h",
//		Granularity: "1h",
//		Start:       &startTime,
//	}
//	allPages, err := measures.List(gnocchiClient, metricID, listOpts).AllPages()
//	if err != nil {
//		panic(err)
//	}
//
//	allMeasures, err := measures.ExtractMeasures(allPages)
//	if err != nil {
//		panic(err)
//	}
//
//	for _, measure := range allMeasures {
//		fmt.Printf("%+v\n", measure)
//	}
//
//}

func (s Server) CreateVncScreenShot(c *gin.Context) {
	SID := c.Param("uuid")
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	logrus.Info("CreateVncScreenShot start, server_id=", SID)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	remoteConsole, err := s.createVncConsole(serverCLient, SID)
	fmt.Println(remoteConsole)
	token := strings.Split(remoteConsole.URL, "%3D")[1]
	con, err := s.getConsoleInfo(serverCLient, token)
	if err != nil {
		c.JSON(500, err)
		logrus.Error("CreateVncScreenShot error, server_id=", SID, ",error=", err)
		return
	}

	snapshotFileName := GetRandomString(32) + ".png"
	logrus.Info("createVncScreenShot, server_id=", SID, ",host=", con.Console.Host, ",port=", con.Console.Port, ",snapshotFilename=", snapshotFileName)
	err = takeSnapshot(con.Console.Host, con.Console.Port, screenshotPath, snapshotFileName)
	if err != nil {
		c.JSON(500, err)
		logrus.Error("CreateVncScreenShot error, server_id=", SID, ",error=", err)
		return
	}
	ret := map[string]string{"filename": snapshotFileName}
	logrus.Info("CreateVncScreenShot end, server_id=", SID, ",snap filename=", snapshotFileName)
	c.JSON(200, ret)
	return
}

func GetRandomString(n int) string {
	var letterRunes = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func takeSnapshot(host string, port int, filepath string, filename string) (err error) {
	var out bytes.Buffer
	var stderr bytes.Buffer
	logrus.Info("CreateScreenshot , cmd = ", "/usr/bin/python3", "/opt/openstack_api/vnc/vnc2png.py", filepath+filename, host, strconv.Itoa(port))
	cmd := exec.Command("/usr/bin/python3", "/opt/openstack_api/vnc/vnc2png.py", filepath+filename, host, strconv.Itoa(port))

	//cmd := exec.Command("ls", "-alh")
	fmt.Println(cmd)
	cmd.Stdout = &out
	cmd.Stderr = &stderr
	err = cmd.Run()
	if err != nil {
		logrus.Error(err.Error(), stderr.String())
		fmt.Println(err.Error(), stderr.String())
	} else {
		logrus.Info(out.String())
		fmt.Println(out.String())
	}
	return
}

func (s Server) createVncConsole(serverClient *gophercloud.ServiceClient, sid string) (remoteConsole *remoteconsoles.RemoteConsole, err error) {
	serverClient.Microversion = "2.6"
	createOpts := remoteconsoles.CreateOpts{
		Protocol: remoteconsoles.ConsoleProtocolVNC,
		Type:     remoteconsoles.ConsoleTypeNoVNC,
	}
	remoteConsole, err = remoteconsoles.Create(serverClient, sid, createOpts).Extract()
	if err != nil {
	}
	return
}

func (s Server) getConsoleInfo(serverClient *gophercloud.ServiceClient, consoleTokens string) (r client.OSConsoleResultStruct, err error) {
	serverClient.Microversion = "2.88"
	//console := client.OSConsoleStruct{}
	//var r client.OSConsoleResultStruct
	err = client.OSConsoleAuthTokens(serverClient, consoleTokens).ExtractInto(&r)

	fmt.Println(r.Console)
	return
}

func (s Server) DeleteVncScreenShot(c *gin.Context) {
	SID := c.Param("uuid")
	imageName := c.Param("filename")
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	_, err = os.Stat(screenshotPath + imageName)
	if err == nil {
		_ = os.Remove(screenshotPath + imageName)
	}
	c.JSON(200, map[string]string{"status": "complete"})
}

func (s Server) GetVncScreenShot(c *gin.Context) {
	SID := c.Param("uuid")
	filename := c.Param("filename")
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	_, err = os.Stat(screenshotPath + filename)
	if err == nil {
		c.File(screenshotPath + filename)
		//c.JSON(200, nil)
		return
	} else {
		c.JSON(404, nil)
		return
	}
	//c.JSON(200, map[string]string{"status": "complete"})
}

func (s Server) CreateVncScreenCap(c *gin.Context) {
	SID := c.Param("uuid")
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	params := ScreenCapParam{}
	err = c.ShouldBindJSON(&params)
	logrus.Info("CreateVncScreenCap start, server_id=", SID, ",params=", params)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}
	remoteConsole, err := s.createVncConsole(serverCLient, SID)
	fmt.Println(remoteConsole)
	token := strings.Split(remoteConsole.URL, "%3D")[1]
	con, err := s.getConsoleInfo(serverCLient, token)
	logrus.Info("CreateVncScreenCap process, server_id=", SID, ",connctions=", con)
	if err != nil {
		c.JSON(500, err)
		logrus.Error(err)
		return
	}

	snapshotFileName := GetRandomString(32) + ".mp4"
	if params.Seconds == 0 {
		params.Seconds = 1800
	}
	logrus.Info("CreateVncScreenCap, server_id=", SID, ",host=", con.Console.Host, ",port=", con.Console.Port, ",snapshotFileName=", snapshotFileName)
	_, err = takeSnapCap(con.Console.Host, con.Console.Port, screencapPath, snapshotFileName, params.Seconds)

	if err != nil {
		c.JSON(500, err)
		logrus.Error(err)
		return
	}
	ret := map[string]string{"filename": snapshotFileName}
	c.JSON(200, ret)
	logrus.Info("CreateVncScreenCap end, server_id=", SID, ",ret=", ret)
	return
}

func (s Server) StopVncScreenCap(c *gin.Context) {
	SID := c.Param("uuid")
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	logrus.Info("StopVncScreenCap start, server_id=", SID)
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	if err != nil {
		s.errRet(c, 500, err.Error())
		return
	}

	remoteConsole, err := s.createVncConsole(serverCLient, SID)
	fmt.Println(remoteConsole)
	token := strings.Split(remoteConsole.URL, "%3D")[1]
	con, err := s.getConsoleInfo(serverCLient, token)
	logrus.Info("StopVncScreenCap start, server_id=", SID, ",connections=", con)
	if err != nil {
		c.JSON(500, err)
		logrus.Error("StopVncScreenCap error, server_id=", SID, ",error=", err)
		return
	}
	err = stopSnapCap(con.Console.Host, con.Console.Port)
	if err != nil {
		c.JSON(500, err)
		logrus.Error("StopVncScreenCap error, server_id=", SID, ",error=", err)
		return
	}
	logrus.Info("StopVncScreenCap end, server_id=", SID)
	c.JSON(200, nil)
}

func (s Server) DeleteVncScreenCap(c *gin.Context) {
	SID := c.Param("uuid")
	filename := c.Param("filename")
	serverCLient := client.GetServerClient()
	logrus.Info("DeleteVncScreenCap start, server_id=", SID, ",filename=", filename)
	server := ServerWithExt{}
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	if err != nil {
		s.errRet(c, 500, err.Error())
		logrus.Info("DeleteVncScreenCap error, server_id=", SID, ",error=", err)
		return
	}
	_, err = os.Stat(screencapPath + filename)
	if err == nil {
		_ = os.Remove(screencapPath + filename)
	}
	c.JSON(200, map[string]string{"status": "complete"})
}

func takeSnapCap(host string, port int, filepath string, filename string, seconds int) (pid int, err error) {

	var out bytes.Buffer
	var stderr bytes.Buffer

	cmd := exec.Command("/usr/bin/python3", "/opt/openstack_api/vnc/vnc2mp4.py", filepath+filename, host, strconv.Itoa(port), "1")
	logrus.Info("Take a snap cap , host=", host, "port=", port, "filename=", filepath+filename)
	//cmd := exec.Command("ls", "-alh")
	fmt.Println(cmd)
	cmd.Stdout = &out
	cmd.Stderr = &stderr
	err = cmd.Start()

	if err != nil {
		logrus.Error("Snap cap err", err.Error(), stderr.String(), filename)
		fmt.Println(err.Error(), stderr.String())
		return
	} else {
		logrus.Info("Snap cap success", out.String(), filename)
		fmt.Println(out.String())
	}
	pid = cmd.Process.Pid
	time.AfterFunc(time.Duration(seconds)*time.Second, func() {
		logrus.Info("Snap cap stop, filename=", filepath+filename)
		err := cmd.Process.Signal(syscall.SIGINT)
		if err != nil {
			return
		}
	})
	return
}

func stopSnapCap(host string, port int) (err error) {
	str := fmt.Sprintf("/usr/bin/kill -2 `ps -ef | grep vnc2mp4 | grep -v grep | grep %s | grep %d | awk  '{print $2}'`", host, port)
	var out bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command("bash", "-c", str)
	cmd.Stdout = &out
	cmd.Stderr = &stderr
	err = cmd.Run()
	fmt.Println(cmd)
	if err != nil {
		logrus.Error("kill vncscreencap process faild, host=", host, "port=", port, err.Error(), stderr.String())
		fmt.Println(err.Error(), stderr.String())
		return
	} else {
		logrus.Info(out.String())
		fmt.Println(out.String())
	}
	return
}

func (s Server) GetVncScreenCap(c *gin.Context) {
	SID := c.Param("uuid")
	filename := c.Param("filename")
	serverCLient := client.GetServerClient()
	server := ServerWithExt{}
	logrus.Info("GetVncScreenCap start, server_id=", SID, ",filename=", filename)
	err := servers.Get(serverCLient, SID).ExtractInto(&server)
	if err != nil {
		s.errRet(c, 500, err.Error())
		logrus.Info("GetVncScreenCap error, server_id=", SID, ",error=", err)
		return
	}
	_, err = os.Stat(screencapPath + filename)
	if err == nil {
		c.File(screencapPath + filename)
		//c.JSON(200, nil)
		return
	} else {
		c.JSON(404, nil)
		logrus.Info("GetVncScreenCap error, server_id=", SID, ",error=", err)
		return
	}
	//c.JSON(200, map[string]string{"status": "complete"})
}
